/**
 * 该文件就是调度器的具体实现
 */

// 这个 只是写了个大概 看 就业课里面的笔记  看自己 笔记
//  看 掘金 https://juejin.cn/post/7171000978278187038?searchId=2025022414383180EAB2FACD170402B1B4 这个文章的解读

import { push, pop, peek } from './SchedulerMinHeap';
import { getCurrentTime } from '../shared/utils';

let startTime = -1; // 当前宏任务开始执行时间

// 普通任务队列
const taskQueue = [];

// 延迟任务队列
var timerQueue = [];

// 任务 id 计数器
let taskIdCounter = 1;

// 是否有剩余时间
let hasTimeRemaining = true;

// 通过 MessageChannel 来模拟浏览器的 requestIdleCallback
const { port1, port2 } = new MessageChannel();

/**
 * 该函数的作用是为了组装一个任务对象，然后将其放入到任务队列
 * @param {*} callback 是一个需要执行的任务，该任务会在每一帧有剩余时间的时候去执行
 */
export default function scheduleCallback(callback) {
  // 获取当前时间
  const currentTime = getCurrentTime();
  // 接下来设置任务过期时间
  // 在 React 源码中，针对不同的任务类型，设定了不同的过期时间
  // 那么我们这里做一个简化，所有任务的优先级都相同
  const timeout = -1; // timeout = 500 ms ？

  // 计算出过期时间
  const expirationTime = currentTime + timeout;

  // 组装一个新的任务对象
  const newTask = {
    id: taskIdCounter++,
    callback,
    expirationTime,
    sortIndex: expirationTime, // 回头会根据这个 sortIndex 来进行排序
  };

  // 将新的任务推入到任务队列
  push(taskQueue, newTask);

  // 接下来请求调度，这样会产生一个宏任务
  port1.postMessage(null);
}

// 每次 port1.postMessage(null) 的时候，就会触发 port2.onmessage
// 在 port2.onmessage 中，我们会去执行任务队列中的任务
port2.onmessage = function () {
  // 该方法的作用就是不停的从任务队列中取出任务，然后执行

  // 获取当前时间
  const currentTime = getCurrentTime();

  startTime = currentTime;
  // 从任务队列中取出第一个任务
  let currentTask = peek(taskQueue);

  // 这个 while 就是将 workloop 包装成一个一个 宏任务 然后传入 这个 当前执行的 任务还有多长时间过期
  while (currentTask) {
    // 首先这里需要做一个时间上面的判断
    // 如果任务的过期时间远大于当前时间（说明当前这个任务不着急，可以延期执行） 说明还没过期
    // 并且当前帧所剩余的时间也不够了，那么就不执行了
    if (currentTask.expirationTime > currentTime && !hasTimeRemaining) {
      // 其实 源码里面 判断是这样的 (currentTask.expirationTime > currentTime && （!hasTimeRemaining || shouldYieldToHost()))
      // shouldYieldToHost 表示 当前任务的执行 已经超过了 5ms 了  应该让出时间片了
      break;
    }

    // 没有进入到上面的 if，说明当前的任务是需要执行的
    const callback = currentTask.callback;
    currentTask.callback = null;
    // 执行对应的任务，传入剩余的时间
    // 传入的时间 就是time 就是  这个任务还有 多长时间 过期
    const taskResult = callback(currentTime - currentTask.expirationTime);

    if (taskResult === undefined) {
      // 进入此 if，说明是任务执行完了才退出来的，那么就可以将其从任务队列中删除了
      pop(taskQueue);
      currentTask = peek(taskQueue);
    }
  }

  // 这里 其实还有一段逻辑 才是完整的  ！！！！！
  // if (taskQueue.length > 0) {
  //   // 把线程交换给 渲染流水线  然后因为这里面还有任务需要执行
  //   // 再创建一个 宏任务 加入到宏任务队列  等浏览器 有空闲时间的时候再执行 当前函数
  //   port1.postMessage(null);
  // }
};

// 当前宏任务执行是否超过了 5ms
function shouldYieldToHost() {
  if (getCurrentTime() - startTime > 5) {
    return true;
  } else {
    return false;
  }
}

export const NoPriority = 0;
export const ImmediatePriority = 1;
export const UserBlockingPriority = 2;
export const NormalPriority = 3;
export const LowPriority = 4;
export const IdlePriority = 5;

var maxSigned31BitInt = 1073741823;

// Times out immediately
var IMMEDIATE_PRIORITY_TIMEOUT = -1;
// Eventually times out
var USER_BLOCKING_PRIORITY_TIMEOUT = 250;
var NORMAL_PRIORITY_TIMEOUT = 5000;
var LOW_PRIORITY_TIMEOUT = 10000;
// Never times out

// 自己实现的原理
// export default function scheduleCallback1(priorityLevel,callback,delay) {
//   // 获取当前时间
//   const currentTime = getCurrentTime();

//   const startTime = currentTime + delay;

//    var timeout;
//   //  不同的优先级 对应不同的过期时间
//     switch (priorityLevel) {
//       case ImmediatePriority:
//         timeout = IMMEDIATE_PRIORITY_TIMEOUT;
//         break;
//       case UserBlockingPriority:
//         timeout = USER_BLOCKING_PRIORITY_TIMEOUT;
//         break;
//       case IdlePriority:
//         timeout = IDLE_PRIORITY_TIMEOUT;
//         break;
//       case LowPriority:
//         timeout = LOW_PRIORITY_TIMEOUT;
//         break;
//       case NormalPriority:
//       default:
//         timeout = NORMAL_PRIORITY_TIMEOUT;
//         break;
//     }

//   // 计算出过期时间
//   const expirationTime = currentTime + timeout;

//   // 组装一个新的任务对象
//   const newTask = {
//     id: taskIdCounter++,
//     callback,
//     expirationTime,
// // 延迟任务根据startTime 排序 普通任务根据expirationTime 排序
//     sortIndex:startTime > currentTime ? startTime:  expirationTime, // 回头会根据这个 sortIndex 来进行排序
//   };

// if(startTime > currentTim){
//   push(timerQueue, newTask);
// }else{
//   // 将新的任务推入到任务队列
//   push(taskQueue, newTask);
// }
//   // 接下来请求调度，这样会产生一个宏任务
//   port1.postMessage(null);
// }
